
#include <ucontext.h>
#include <stdio.h>
#include <stdlib.h>

#include "uthread.h"
#include "linked.h"
#include "queue.h"
#include "util.h"

#ifndef STACK_SIZE
#define STACK_SIZE 1048576
#endif

static volatile int iD = 0;
static queue_t *queue;
static queue_t *deleted; 
void make_stack(ucontext_t* context)
{
    char* stack = malloc(STACK_SIZE);
    CHECK_PTR(stack);
    CHECK_ARG(context);
    context->uc_stack.ss_flags = 0;
    context->uc_stack.ss_sp = stack;

    context->uc_stack.ss_size = sizeof stack;
}

ucontext_t *acquire_context()
{
    ucontext_t* context = malloc(sizeof(ucontext_t));
    CHECK_PTR(context);
    make_stack(context);
    if(getcontext(context)==-1){
        die("failed to getcontext in acquire_context()");
    }
    context->uc_link = NULL;
    if(context == NULL)
    {
        die("Failed to acquire context.");
    }
    return context; 
}

int uthread_create(void (*func)(int), int val, int priority)
{
    ucontext_t* oldcontext = acquire_context();
    //make_stack(oldcontext);
    ucontext_t* link = queue->current->context;
    oldcontext->uc_link = link;
    makecontext(oldcontext, (void (*)(void)) func, 1, val);
    queue_push_context(queue, oldcontext);
    return iD++;
}
ucontext_t* deleter_context;
linked_t* swap_node = NULL;
void delete_func()
{
    while(true)
    {
        
        if (!queue_is_empty(deleted))
        {
            linked_t* delnode = queue_pop(deleted);
            //printf("We are deleting (%d) from (%d)\n", delnode->id, swap_node->id);
            if (delnode->context == NULL)
            {
                die("null context\n");
            }
            //delnode->context->uc_stack.ss_sp;
            //free(delnode->context->uc_stack.ss_sp);
            
            free(delnode->context);
            free(delnode);
            
        }
        
        swapcontext(deleter_context, swap_node->context);
    }
    
    
}

void create_deleter()
{
    deleter_context = acquire_context();
    makecontext(deleter_context, &delete_func, 0); 
}

void uthread_init()
{
    queue = queue_create();
    deleted = queue_create();
    ucontext_t *rootContext = acquire_context();
    
    queue_push_context(queue, rootContext);
    create_deleter();
}



void uthread_yield()
{
    
    if(queue_is_empty(queue))
    {
        die("Yielded from nonexistent thread.");
    }
    
    
    linked_t *rmHead = queue_pop(queue);
    queue_insert(queue, rmHead);
    linked_t *newHead = queue->current; 
    if(newHead->context == NULL)
    {
        die("Yielded from null context\n", newHead->id);
    }
    
    swapcontext(rmHead->context, newHead->context);
}

void uthread_exit()
{
    if(queue_is_empty(queue)==true)
    {
        die("Thread exited when no running thread remained.\n");
    }
    
    linked_t *rmHead = queue_pop(queue);
    swap_node = rmHead;
    swapcontext(rmHead->context, deleter_context);
    
    queue_insert(deleted, rmHead);
    linked_t* next = queue->current;
    rmHead->context->uc_link = next->context;
    if(queue_is_empty(queue)==true)
    {
        exit(0);
    }
    
    /*linked_t *peeker = queue->current;
    
    //setcontext(peeker->context);
    swapcontext(rmHead->context, peeker->context);
    //die("setcontext failure in uthread_exit");
    //peeker->context->uc_link = rmHead->context;
    return;*/
    
}













